package com.heima.lock.controller;

import com.heima.lock.mapper.MyLockMapper;
import com.heima.lock.pojo.MyLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 陈辉
 * @data 2024 15:12
 * <p>
 * 模拟秒杀业务场景： 下单，扣库存
 */
@RestController
public class StockController {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private MyLockMapper lockMapper;
    @Autowired
    private RedissonClient redissonClient;

    /**
     * synchronized只能锁住单个JVM内线程资源，不能锁住多JVM之间的线程。
     *
     * @return
     */
    @GetMapping("/stock1")
    public String stock1() {
        //1. 查询商品现有库存数据
        synchronized ("abc") {
            Integer stock = Integer.valueOf(redisTemplate.opsForValue().get("stock"));
            //2. 判断当前库存是否足够
            if (stock > 0) {
                //2.1 足够：下单成功,将当前库存 -1
                System.out.println("恭喜你，下单成功!当前订单序号：" + stock);

                stock--;
                redisTemplate.opsForValue().set("stock", String.valueOf(stock));
                return "success";
            } else {
                //2.2 不够：手慢了，商品卖完了~
                System.out.println("手慢了，商品卖完了~,剩余库存：" + stock);
                return "fail";
            }
        }
    }

    /**
     * 基于mysql来实现分布式锁
     *
     * @return
     */
    @GetMapping("/stock2")
    public String stock2() {
        //1. 尝试获得锁
        MyLock myLock = new MyLock();
        myLock.setLockName("myLock");
        myLock.setLockValue(String.valueOf(Thread.currentThread().getId()));
        try {
            lockMapper.insert(myLock);

            //2. 拿到锁：执行业务代码
            Integer stock = Integer.valueOf(redisTemplate.opsForValue().get("stock"));
            if (stock > 0) {
                //2.1 足够：下单成功,将当前库存 -1
                System.out.println("恭喜你，下单成功!当前订单序号：" + stock);
                stock--;
                redisTemplate.opsForValue().set("stock", String.valueOf(stock));
                return "success";
            } else {
                //2.2 不够：手慢了，商品卖完了~
                System.out.println("手慢了，商品卖完了~,剩余库存：" + stock);
                return "fail";
            }
        } catch (Exception e) {
            //4. 没有抢到锁：提示：网络正忙，稍后再试!
            System.out.println("获取分布式锁失败，网络正忙，稍后再试!");
            return "fail";
        } finally {
            //3. 释放锁
            //判断当前线程是否持有锁，持有则释放锁
            if (String.valueOf(Thread.currentThread().getId()).equals(myLock.getLockValue()) && myLock.getId() != null) {
                lockMapper.deleteById(myLock.getId());
            }
        }

    }


    /**
     * 基于redis来实现分布式锁
     *
     * @return
     */
    @GetMapping("/stock3")
    public String stock3() {
        //1. 尝试获得锁
        Boolean isLock = redisTemplate.opsForValue().setIfAbsent("myLock", String.valueOf(Thread.currentThread().getId()));// setNX
        if (isLock) {
            try {
                //2. 拿到锁：执行业务代码
                Integer stock = Integer.valueOf(redisTemplate.opsForValue().get("stock"));
                if (stock > 0) {
                    //2.1 足够：下单成功,将当前库存 -1
                    System.out.println("恭喜你，下单成功!当前订单序号：" + stock);
                    stock--;
                    redisTemplate.opsForValue().set("stock", String.valueOf(stock));
                    return "success";
                } else {
                    //2.2 不够：手慢了，商品卖完了~
                    System.out.println("手慢了，商品卖完了~,剩余库存：" + stock);
                    return "fail";
                }
            } finally {
                //3. 释放锁
                //判断当前线程是否持有锁，持有则释放锁
                String threadId = redisTemplate.opsForValue().get("myLock");
                if (String.valueOf(Thread.currentThread().getId()).equals(threadId)) {
                    redisTemplate.delete("myLock");
                }
            }
        } else {
            //4. 没有抢到锁：提示：网络正忙，稍后再试!
            System.out.println("获取分布式锁失败，网络正忙，稍后再试!");
            return "fail";
        }

    }


    /**
     * 基于redisson实现分布式锁
     * 阻塞锁lock   /非阻塞锁  tryLock
     *
     * @return
     */
    @GetMapping("/stock4")
    public String stock4() {
        //1. 尝试获得锁
        //1.1 这里仅仅是创建了一个java对象
        RLock lock = redissonClient.getLock("myLock");
        lock.lock();   //  尝试获得锁,这里是阻塞的!!!
        try {
            Thread.sleep(30000);
            //2. 拿到锁：执行业务代码
            Integer stock = Integer.valueOf(redisTemplate.opsForValue().get("stock"));
            if (stock > 0) {
                //2.1 足够：下单成功,将当前库存 -1
                System.out.println("恭喜你，下单成功!当前订单序号：" + stock);
                stock--;
                redisTemplate.opsForValue().set("stock", String.valueOf(stock));
                return "success";
            } else {
                //2.2 不够：手慢了，商品卖完了~
                System.out.println("手慢了，商品卖完了~,剩余库存：" + stock);
                return "fail";
            }
        } catch (InterruptedException e) {
            return "fail";
        } finally {
            //3. 释放锁
            //判断当前线程是否持有锁，持有则释放锁
            lock.unlock();
        }
    }


    /**
     * 基于redisson实现分布式锁
     * 公平锁
     *
     * @return
     */
    @GetMapping("/stock5")
    public String stock5() {
        //1. 尝试获得锁
        //1.1 这里仅仅是创建了一个java对象
        RLock lock = redissonClient.getFairLock("myLock");
        lock.lock();   //  尝试获得锁,这里是阻塞的!!!

        try {
            //2. 拿到锁：执行业务代码
            Integer stock = Integer.valueOf(redisTemplate.opsForValue().get("stock"));
            if (stock > 0) {
                //2.1 足够：下单成功,将当前库存 -1
                System.out.println("恭喜你，下单成功!当前订单序号：" + stock);
                stock--;
                redisTemplate.opsForValue().set("stock", String.valueOf(stock));
                return "success";
            } else {
                //2.2 不够：手慢了，商品卖完了~
                System.out.println("手慢了，商品卖完了~,剩余库存：" + stock);
                return "fail";
            }
        } finally {
            //3. 释放锁
            //判断当前线程是否持有锁，持有则释放锁
            lock.unlock();
        }

    }
}
